/*
 * Copyright (c) 2012 Sean Connelly
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

var config = require('./config.js');
var common = require('./common.js');

//
// error system for compiler
//

var msgFmts = {
		// general errors
		Internal: 'RE010001: an unexpected internal error occurred due to an unknown bug ($@)',
		// lex-rho.js
		IdentifierTooLong: 'RE010002: identifier "$0" with $1 characters is too long (maximum ' +
			config.maxIdentLength + ' characters)',
		InvalidCharacter: 'RE010003: invalid character "$0"',
		MissingEndCommentBlock: 'RE010004: missing end comment "' + config.blockCommentEnd + '"',
		CannotNestBlockComment: 'RE010005: cannot nest block comments',
		MissingEndCommentNest: 'RE010006: missing end comment "' + config.nestCommentEnd + '"',
		MissingVersion: 'RE010007: missing version at beginning of file; enter "' +
			config.versionPrefix + config.versionText + '" as first line of program',
		UnterminatedString: 'RE010008: string missing end quote',
// OLD VERSIONS:
/*
		// LexVersion, LexVersionParts errors
		MissingMinorVersion: 'invalid version "$0"; minor version number required, i.e., "' + config.versionText + '"',
		InvalidVersion: 'invalid version "$0" at beginning of file; enter "' + config.versionText + '" as first line of program',
		// LexStringParts errors
		InvalidString: 'invalid string: $0',
		StringCannotContainNewlines: 'invalid string; strings not allowed to contain newlines',
		InvalidEscapeSequence: 'invalid string escape sequence: $0',
		// LexRatioParts errors
		InvalidNumber: 'invalid number: $0',
		InvalidLeadingZero: 'number "$0" cannot start with a leading "0" (octal numbers start with "0c")',
		// LexMacroTextParts errors
		InvalidMacroText: 'invalid macro text',
		// LexRho errors
		UnterminatedMacro: 'macro missing end brace',
		// Parser errors
		InvalidStatement: 'invalid statement',
		MissingKeyword: 'invalid statement; missing keyword "$0"',
		MissingKeywordList: 'invalid statement; expecting one of: $0',
		InvalidKeyword: 'invalid statement; missing keyword "$0", instead found "$1"',
		InvalidKeywordList: 'invalid statement; found keyword "$1", expecting one of: $0',
		MissingSpecial: 'invalid statement; missing "$0"',
		MissingSpecialList: 'invalid statement; expecting one of: $0',
		InvalidSpecial: 'invalid statement; missing "$0", instead found "$1"',
		InvalidSpecialList: 'invalid statement; found "$1", expecting one of: $0',
		MissingIdent: 'invalid statement; missing identifier',
		InvalidExpression: 'invalid expression',
		InvalidDefinition: 'invalid definition',
		InvalidMetatype: 'invalid metatype "$0"',
		DefinesMustBeAtTop: 'define statements must be before executable statements',
		ImportsMustBeAtTop: 'import statements must be before executable statements',
		InvalidThenElse: 'invalid "then-else" construct; must be in the format of `a then b else c`',
		HereLabelAlreadyDefined: 'label >>here cannot be redefined; it is always set to the next statement after the statement it\'s used in',
		ExpectingMy: 'missing `my` after `reset` keyword',
		ExpectingDeclarationInClass: 'invalid class statement; expecting declaration',
		MaxErrors: 'too many errors; aborting',
		// Interpret errors
		MetatypeCannotBeTemplated: 'metatype "$0" cannot have a template',
		VariableUnionCannotHaveTypes: 'variable union metatypes cannot have additional types',
		NumLowMustBeWhole: 'low value in metatype "$0" must be a whole number',
		NumHighMustBeWhole: 'high value in metatype "$0" must be a whole number',
		NumReversedRange: 'metatype "$0" has range in wrong order; lower value is always first',
		NumBitsMustBeWhole: 'bit size in metatype "$0" must be a whole number',
		NumBitsInvalidSize: 'bit size in metatype "$0" is invalid (expecting 32 or 64 only)',
		TypeNotDefined: '"$0" is not defined; expecting type',
		IdentIsNotType: 'expecting type; instead found "$0"',
		VarNotDefined: '"$0" is not defined; expecting variable',
		IdentIsNotVar: 'expecting variable; instead found "$0"',
		IdentAlreadyDefined: '"$0" is already defined',
		IdentNotInParent: 'invalid `override`; "$0" is not in parent',
		InvalidLvalue: 'invalid assignment; expecting variable',
		ValueNotWrapped: 'invalid unwrap ("@") operator; cannot unwrap a non-wrapped value',
		InvalidMember: 'invalid member "$0"',
		InvalidUseOfMy: 'invalid use of `my` object',
		CannotCompareDifferentTypes: 'type mismatch; cannot compare different types'
*/
	};

// must redirect through a function to get the closures
var genFunc = function(name, msg, ec) {
		return function() {
				var m = msg;
				if (arguments.length > 0 && m.indexOf('$@') >= 0)
				{
					// quickly hash the internal error
					// this does nothing except give a point of reference if users submit the internal
					// error, so that it can be back-tracked to the right error
					var num = 12389;
					var inn = arguments[0];
					for (var i = 0; i < inn.length; i++)
						num = ((num * 231) + inn.charCodeAt(i)) % 65535;
					m = common.replaceAll(m, '$@', '' + num);
				}
				for (var i = arguments.length - 1; i >= 0; i--) // reverse order so $10 is replaced before $1
					m = common.replaceAll(m, '$' + i, arguments[i]);
				return {
						code: ec,
						message: m
					};
			};
	};

var re = new RegExp('^([^:]+): (.*)$');
for (var err in msgFmts)
{
	var sp = msgFmts[err].match(re);
	if (sp === null)
		throw 'invalid error: ' + msgFmts[err];
	else
		exports[err] = genFunc(err, sp[2], sp[1]);
}

